package com.feixiang.springbootinit.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.Arrays;

@Slf4j
/**
 * RSA工具类
 */
public class RSAUtil {
    /**
     * RSA私钥解密
     *
     * @param str        加密字符串
     * @param privateKey 私钥
     * @return 铭文
     * @throws Exception 解密过程中的异常信息
     */
    public static String decrypt(String str, String privateKey) {
        //log.info("密文:{}", str);
        //log.info("私钥:{}", privateKey);
        try {
            // 调用getPrivateKey解析私钥字符串
            PrivateKey key = getPrivateKey(privateKey);
            return getPrivateKeyByPKCS1(str, key);
        } catch (Exception e) {
            log.error("RSA解密失败", e);
            throw new RuntimeException("RSA解密失败", e);
        }
    }

    /**
     * 用此方法先获取秘钥
     * RSAUtil_PKCS1
     *
     * @param encrypt_random_key 加密的随机密钥
     * @param privateKey         私钥对象
     * @return 解密后的随机密钥
     * @throws Exception 解密过程中的异常信息
     */
    public static String getPrivateKeyByPKCS1(String encrypt_random_key, PrivateKey privateKey) throws Exception {
        // 64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(encrypt_random_key.getBytes("UTF-8"));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // RSA解密 - 当长度过长的时候，需要分割后解密
        String outStr = new String(getMaxResultDecrypt(inputByte, cipher));
        //log.info("RSA私钥解密后的数据|outStr:{}", outStr);
        return outStr;
    }

    /**
     * 过长的数据进行分组解密
     *
     * @param encryptedBytes 加密的字节数组
     * @param cipher         初始化好的Cipher对象
     * @return 解密后的字节数组
     */
    private static byte[] getMaxResultDecrypt(byte[] encryptedBytes, Cipher cipher) throws Exception {
        int inputLength = encryptedBytes.length;
        //log.info("解密字节数|inputLength:{}", inputLength);
        // 动态计算最大解密块大小（基于密钥长度）
        int maxBlockSize = cipher.getOutputSize(0);
        // 标识
        int offSet = 0;
        byte[] resultBytes = new byte[0];
        byte[] cache;

        while (inputLength - offSet > 0) {
            if (inputLength - offSet > maxBlockSize) {
                cache = cipher.doFinal(encryptedBytes, offSet, maxBlockSize);
                offSet += maxBlockSize;
            } else {
                cache = cipher.doFinal(encryptedBytes, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            // 合并解密结果
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }

        return resultBytes;
    }

    /**
     * 从PKCS#1格式的PEM字符串解析私钥
     *
     * @param privKeyPEM PEM格式的私钥字符串
     * @return 解析后的PrivateKey对象
     * @throws Exception 如果私钥格式无效
     */
    private static PrivateKey getPrivateKey(String privKeyPEM) throws Exception {
        // 清理PEM格式，移除页眉、页脚和换行符
        String keyContent = privKeyPEM
                .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .replace("-----END RSA PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");
        // Base64解码为DER格式的字节数组
        byte[] encodedKey = Base64.decodeBase64(keyContent);
        // 解析PKCS#1格式的私钥
        DerInputStream derReader = new DerInputStream(encodedKey);
        DerValue[] seq = derReader.getSequence(0);
        if (seq.length < 9) {
            throw new IllegalArgumentException("PKCS#1私钥格式不正确，至少需要9个字段");
        }
        // 提取RSA私钥组件
        BigInteger modulus = seq[1].getBigInteger();
        BigInteger publicExponent = seq[2].getBigInteger();
        BigInteger privateExponent = seq[3].getBigInteger();
        BigInteger prime1 = seq[4].getBigInteger();
        BigInteger prime2 = seq[5].getBigInteger();
        BigInteger exponent1 = seq[6].getBigInteger();
        BigInteger exponent2 = seq[7].getBigInteger();
        BigInteger coefficient = seq[8].getBigInteger();
        // 生成私钥
        RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec(
                modulus, publicExponent, privateExponent,
                prime1, prime2, exponent1, exponent2, coefficient
        );
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }
}